package test.baojia.act;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.exceptions.TemplateInputException;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

public abstract class AbstractAct implements Act {

    public final static String CHARSET_FORMAT = "UTF-8";

    protected final String GLOBAL = "global";

    protected JSONObject context = null;

    protected JSONObject parentContext = null;

    protected WebDriver driver = null;

    protected Properties resource = null;

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    protected final static String MAIN_FRAME_ID = "mainFrame";

    protected final static By MAIN_FRAME_BY = By.id(MAIN_FRAME_ID);

    protected final static String MAIN_FRAME_NAME = "body > ul > li.breadcrumb-item.active";

    protected final static By MAIN_FRAME_BY_NAME = By.cssSelector(MAIN_FRAME_NAME);

    protected final static ClassLoaderTemplateResolver JS_TEMPLATE_RESOLVER = new ClassLoaderTemplateResolver();

    protected final static ClassLoaderTemplateResolver BUSINESS_TEMPLATE_RESOLVER = new ClassLoaderTemplateResolver();

    protected final static String ACT_SUB_PACKAGE_TEMPLATE = Act.class.getPackage().getName() + ".%s.%s";

    static{
        JS_TEMPLATE_RESOLVER.setTemplateMode(TemplateMode.JAVASCRIPT);
        JS_TEMPLATE_RESOLVER.setPrefix("templates/js/");
        JS_TEMPLATE_RESOLVER.setSuffix(".js");
        JS_TEMPLATE_RESOLVER.setCharacterEncoding(CHARSET_FORMAT);
        JS_TEMPLATE_RESOLVER.setCacheTTLMs(Long.valueOf(3600000L));
        JS_TEMPLATE_RESOLVER.setCacheable(true);

        BUSINESS_TEMPLATE_RESOLVER.setTemplateMode(TemplateMode.TEXT);
        BUSINESS_TEMPLATE_RESOLVER.setPrefix("templates/batchTestJson/");
        BUSINESS_TEMPLATE_RESOLVER.setSuffix(".json");
        BUSINESS_TEMPLATE_RESOLVER.setCharacterEncoding(CHARSET_FORMAT);
        BUSINESS_TEMPLATE_RESOLVER.setCacheTTLMs(Long.valueOf(3600000L));
        BUSINESS_TEMPLATE_RESOLVER.setCacheable(true);
    }

    public void run(JSONObject context, WebDriver driver, Properties resource, JSONObject parentContext) throws Exception {
        this.context = context;
        this.driver = driver;
        this.resource = resource;
        this.parentContext = parentContext;
    }

    protected final WebDriverWait newWebDriverWait(String waitName){
        if(StringUtils.isBlank(waitName)){
            waitName = "wait.default.config";
        }

        final String waitConfig = resource.getProperty(waitName);
        final String[] waitConfigs = waitConfig.split(";");

        return new WebDriverWait(driver , Integer.valueOf(waitConfigs[0]) , Integer.valueOf(waitConfigs[1]));
    }

    protected final void toMainFrame(){
        driver.switchTo().defaultContent();
        driver.switchTo().frame(MAIN_FRAME_ID);
    }

    protected final String toJsScript(String templateName , Map<String , Object> context){
        final Context templateContext = new Context(Locale.getDefault() , context);

        TemplateEngine te = new TemplateEngine();
        te.setTemplateResolver(JS_TEMPLATE_RESOLVER);

        return te.process(templateName , templateContext);
    }

    private final String makeSimpleTemplateName(Object o){
        return o.getClass().getSimpleName();
    }

    protected final String makeDefaultJsScript(Map<String, Object> context){
        Object templateObject = this;
        String script = null;

        while(StringUtils.isBlank(script)){
            final String templateName = makeSimpleTemplateName(templateObject);
            try{
                script = this.toJsScript(templateName , context);
            }catch(TemplateInputException e){
                Class superClass = templateObject.getClass().getSuperclass();
                if(this.getClass().equals(superClass)){
                    break;
                }
                else{
                    try {
                        templateObject = superClass.newInstance();
                    } catch (Exception ex) {
                        break;
                    }
                }
            }
        }
        Assert.assertTrue(StringUtils.isNotBlank(script) , "Load js failed.");
        return script;
    }

    protected final void runDefaultJsScript(Object o , Map<String , Object> context){
        final String templateName = makeSimpleTemplateName(o);
        this.runJsScript(templateName , context);
    }

    protected final void runJsScript(String templateName , Map<String , Object> context){
        final String script = this.toJsScript(templateName , context);
        this.logger.info("Js template name : {}" , templateName);
        this.logger.info(script);
        JavascriptExecutor je = (JavascriptExecutor) driver;
        je.executeScript(script);
    }

    public static Act loadAct(String actName , String actType){
        final String fullClassName = String.format(ACT_SUB_PACKAGE_TEMPLATE , actType , actName);
        try {
            return (Act) Act.class.getClassLoader().loadClass(fullClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    protected void scroll2ElementTop(WebElement we){
        int top = we.getLocation().getY();
        if(top > 200){
            top -= 100;
        }
        Map<String , Object> context = new HashMap();
        context.put("top" , top);

        this.runJsScript("Scroll" , context);
    }

    protected final void runDefaultTestJson(Object o , Map<String , Object> context , WebDriver driver, Properties resource , JSONObject parentContext) throws  Exception {
        final String templateName = makeSimpleTemplateName(o);
        this.runTestJson(templateName , context , driver , resource , parentContext);
    }

    protected final void runTestJson(
            String templateName , Map<String , Object> context , WebDriver driver, Properties resource , JSONObject parentContext) throws  Exception {
        final String json = this.toTestJson(templateName , context);
        this.logger.info("TestJson template name : {}" , templateName);
        this.logger.info(json);
        JsonScriptRunner.runScript(JSONObject.parseObject(json) , driver , resource , false , parentContext);
    }

    protected final String toTestJson(String templateName , Map<String , Object> context){
        final Context templateContext = new Context(Locale.getDefault() , context);

        TemplateEngine te = new TemplateEngine();
        te.setTemplateResolver(BUSINESS_TEMPLATE_RESOLVER);

        return te.process(templateName , templateContext);
    }

    protected void putGlobalProperty(String key , String value){
        resource.put(this.makeGlobalKey(key) , value);
    }

    protected String getGlobalProperty(String key){
        return resource.getProperty(this.makeGlobalKey(key));
    }

    private String makeGlobalKey(String key){
        return GLOBAL + "." + key;
    }
}
